#include "RandomGsl.h"
#include "MathUtil.h"
#include <stdlib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_randist.h>
#include <iostream>
#include "defs.h"
namespace jcrada {

    RandomGsl RandomGsl::RNG_SEED(static_cast<unsigned int> (std::time(0)));

    RandomGsl::RandomGsl() : Random() {
        this->init();
#ifdef STOCHASTIC
        gsl_rng_set(_rng, RNG_SEED.nextInt());
#else
        BDEBUG("RandomGsl is NOT random!");
        gsl_rng_set(_rng, 0);
#endif
    }

    RandomGsl::RandomGsl(unsigned int seed) : Random(seed) {
        this->init();
        gsl_rng_set(_rng, seed);
    }

    RandomGsl::~RandomGsl() {
        gsl_rng_free(_rng);
    }

    void RandomGsl::init() {
        gsl_rng_env_setup();
        _rng_type = gsl_rng_mt19937;
        _rng = gsl_rng_alloc(_rng_type);
    }

    bool RandomGsl::nextBool() {
        return nextDouble() < 0.5;
    }

    double RandomGsl::nextDouble(double min, double max) {
        return Math::scale(0, 1, nextDouble(), min, max);
    }

    double RandomGsl::nextDouble() {
        return gsl_rng_uniform(_rng);
    }

    float RandomGsl::nextFloat(float min, float max) {
        return Math::scale(0, 1, nextFloat(), min, max);
    }

    float RandomGsl::nextFloat() {
        return (float) gsl_rng_uniform(_rng);
    }

    //    Retorna valor en rango [min,max);

    int RandomGsl::nextInt(int min, int max) {
        return static_cast<int> (Math::scale(0, UINT_MAX / 2, nextInt(), min, max));
    }

    int RandomGsl::nextInt() {
        return gsl_rng_uniform_int(_rng, UINT_MAX / 2);
    }

    long RandomGsl::nextLong(long min, long max) {
        return Math::scale(0, ULONG_MAX / 2, nextLong(), min, max);
    }

    long RandomGsl::nextLong() {
        return gsl_rng_uniform_int(_rng, ULONG_MAX / 2);
    }

    int RandomGsl::nextPoisson(double mu) {
        return gsl_ran_poisson(_rng, mu);
    }

    int RandomGsl::nextPoisson(unsigned int max){
        return gsl_ran_poisson(_rng, max / 2.0) % (max );
    }

    void RandomGsl::setSeed(long seed) {
        gsl_rng_set(_rng, seed);
    }

    //    void RandomGsl::test() {
    //        RandomGsl r;
    //        double mu = 4.0;
    //
    //        //        std::cout << "nextBool() : " << nextBool() << std::endl;
    //        //        std::cout << "nextBool() : " << nextBool() << std::endl;
    //        //
    //        //        std::cout << "nextDouble() : " << nextDouble() << std::endl;
    //        //        std::cout << "nextDouble(0,10) : " << nextDouble(0, 10) << std::endl;
    //        //        std::cout << "nextDouble(-10,10) : " << nextDouble(-10, 10) << std::endl;
    //        //        std::cout << "nextDouble(100,1000) : " << nextDouble(100, 1000) << std::endl;
    //        //        std::cout << "nextDouble(-100,100) : " << nextDouble(-100, 100) << std::endl;
    //        //
    //        //        std::cout << "nextFloat() : " << nextFloat() << std::endl;
    //        //        std::cout << "nextFloat(0,10) : " << nextFloat(0, 10) << std::endl;
    //        //        std::cout << "nextFloat(-10,10) : " << nextFloat(-10, 10) << std::endl;
    //        //        std::cout << "nextFloat(100,1000) : " << nextFloat(100, 1000) << std::endl;
    //        //        std::cout << "nextFloat(-100,100) : " << nextFloat(-100, 100) << std::endl;
    //        //
    //        //        std::cout << "nextInt() : " << nextInt() << std::endl;
    //        //        std::cout << "nextInt(0,10) : " << nextInt(0, 10) << std::endl;
    //        //        std::cout << "nextInt(-10,10) : " << nextInt(-10, 10) << std::endl;
    //        //        std::cout << "nextInt(100,1000) : " << nextInt(100, 1000) << std::endl;
    //        //        std::cout << "nextInt(-100,100) : " << nextInt(-100, 100) << std::endl;
    //        //
    //        //        std::cout << "nextLong() : " << nextLong() << std::endl;
    //        //        std::cout << "nextLong(0,10) : " << nextLong(0, 10) << std::endl;
    //        //        std::cout << "nextLong(-10,10) : " << nextLong(-10, 10) << std::endl;
    //        //        std::cout << "nextLong(100,1000) : " << nextLong(100, 1000) << std::endl;
    //        //        std::cout << "nextLong(-100,100) : " << nextLong(-100, 100) << std::endl;
    //        //
    //        //        int size = 10;
    //        //        int tests = 100000;
    //        //        int * int_array = new int[size];
    //        //        double * dbl_array = new double [size];
    //        //        for (int i = 0; i < size; i++) {
    //        //            int_array[i] = 0;
    //        //            dbl_array[i] = 0;
    //        //        }
    //        //        for (int i = 0; i < tests; i++) {
    //        //            int_array[nextInt(0, size)]++;
    //        //            dbl_array[static_cast<int> (nextDouble(0, size))]++;
    //        //        }
    //        //        for (int i = 0; i < size; i++) {
    //        //            std::cout << i << ": " << int_array[i] << std::endl;
    //        //        }
    //        //        for (int i = 0; i < size; i++) {
    //        //            std::cout << i << ": " << dbl_array[i] << std::endl;
    //        //        }
    //        //        delete [] int_array;
    //        //        delete [] dbl_array;
    //
    //    }

    int RandomGsl::main(int argc, char** argv) {
        RandomGsl random;
        unsigned int max = 5;
        int* probs = new int[max];
        for (int i = 0; i < max; ++i) {
            probs[i] = 0;
        }
        for (int i = 0; i < 100000; ++i) {
            ++probs[random.nextPoisson(max)];
        }
        for (int i = 0; i < max; ++i) {
            BDEBUG(TO_STRING(i) + ": " + TO_STRING(probs[i]));
        }
        for (int i = 0; i < max; ++i) {
            probs[i] = 0;
        }
        for (int i = 0; i < 100000; ++i) {
            ++probs[random.nextInt(0,max)];
        }
        for (int i = 0; i < max; ++i) {
            BDEBUG(TO_STRING(i) + ": " + TO_STRING(probs[i]));
        }
        return 0;
    }



}
